<!--
 * @Descripttion: 
 * @Author: jhw
 * @Date: 2024-06-19 11:35:36
 * @LastEditors: jhw
 * @LastEditTime: 2024-06-20 10:28:37
-->
<template>
  <div>
    <div ref="echartsRef" style="width: 800px; height: 600px"></div>
  </div>
</template>

<script setup lang="ts">
import * as echarts from 'echarts/core';
import { BarChart, LineChart, PieChart, ScatterChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  // 数据集组件
  DatasetComponent,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import type {
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption, // 柱状图
  LineSeriesOption, // 折线图
  PieSeriesOption, // 饼图
  SankeySeriesOption, // 桑基图
} from 'echarts/charts';
import type {
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  DatasetComponentOption,
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';
import { ExtractPublicPropTypes, PropType, markRaw, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { isEmpty } from 'lodash';

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | PieSeriesOption
  | SankeySeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
>;

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LineChart,
  PieChart,
  ScatterChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
]);

const echartsRef = ref();
let chart: echarts.ECharts | null = null;

const props = defineProps({
  width: {
    type: [Number, String],
    default: 600,
  },
  height: {
    type: [Number, String],
    default: 400,
  },
  options: {
    type: Object as PropType<ECOption>,
    required: true,
  },
  transform: {
    type: String,
    default: '',
  },
});

watch(
  () => props.options,
  (value) => {
    if (!isEmpty(value)) {
      nextTick(() => {
        if (chart) {
          // 原画布存在先清空
          chart.clear();
          chart.setOption(value, true);
        } else {
          initCharts();
        }
      });
    }
  },
  { immediate: true },
);

const initCharts = () => {
  chart = markRaw(echarts.init(echartsRef.value));
  // 可选，是否不跟之前设置的 option 进行合并，默认为 false，即合并
  chart.setOption(props.options, true);
};

const handleWindowResize = async () => {
  if (!chart) return;
  let rect = echartsRef.value.getBoundingClientRect();
  await nextTick();
  chart.resize({
    width: rect.width,
    height: rect.height,
  });
};

onMounted(() => {
  window && window.addEventListener('resize', handleWindowResize);
});

onBeforeUnmount(() => {
  window && window.removeEventListener('resize', handleWindowResize);
});

// 将ExtractPublicPropTypes<typeof props>中属性类型都去掉undefined

type NonUndefined<T> = T extends undefined ? never : T;

type RemoveUndefined<T> = {
  [K in keyof T]: NonUndefined<T[K]>;
};

export type EchartsProps = RemoveUndefined<ExtractPublicPropTypes<typeof props>>;

export type EchartsPropsOptions = Exclude<EchartsProps['options'], undefined>;
</script>

<style scoped></style>
